home *** CD-ROM | disk | FTP | other *** search
/ Cream of the Crop 1 / Cream of the Crop 1.iso / PROGRAM / GIFLIB12.ARJ / GIF2EPSN.C < prev    next >
C/C++ Source or Header  |  1991-05-12  |  21KB  |  591 lines

  1. /*****************************************************************************
  2. *   "Gif-Lib" - Yet another gif library.                     *
  3. *                                         *
  4. * Written by:  Gershon Elber                Ver 0.1, Jul. 1989   *
  5. ******************************************************************************
  6. * Program to dump GIF file into EPSON type printers                 *
  7. * Options:                                     *
  8. * -q : quite printing mode.                             *
  9. * -d factor : use dithering of matrix of size factor by factor.             *
  10. * -t level : set the threshold level of white in the result (0..100).         *
  11. * -m mapping : methods for mapping the 24bits colors into 1 BW bit.         *
  12. * -p printer : specify printer to print to (lpt1: by default).             *
  13. * -n : nice mode : uses double density to achieve better quality.         *
  14. * -i : invert the image.                             *
  15. * -h : on line help.                                 *
  16. ******************************************************************************
  17. * History:                                     *
  18. * 15 Jul 89 - Version 1.0 by Gershon Elber.                     *
  19. * 22 Dec 89 - Fix problems with const strings been modified (Version 1.1).   *
  20. *****************************************************************************/
  21.  
  22. #ifdef __MSDOS__
  23. #include <graphics.h>
  24. #include <stdlib.h>
  25. #include <alloc.h>
  26. #include <io.h>
  27. #include <dos.h>
  28. #include <bios.h>
  29. #endif /* __MSDOS__ */
  30.  
  31. #include <stdio.h>
  32. #include <ctype.h>
  33. #include <string.h>
  34. #include <fcntl.h>
  35. #include "gif_lib.h"
  36. #include "getarg.h"
  37.  
  38. #define PROGRAM_NAME    "Gif2Epsn"
  39.  
  40. #define    C2BW_BACK_GROUND    0 /*Methods to map 24bits Colors to 1 BW bit.*/
  41. #define    C2BW_GREY_LEVELS    1
  42. #define C2BW_DITHER        2
  43. #define C2BW_NUM_METHODS    3            /* Always hold # of methods. */
  44.  
  45. #define DEFAULT_THRESHOLD    5000         /* Color -> BW threshold level. */
  46.  
  47. #define DITHER_MIN_MATRIX    2
  48. #define DITHER_MAX_MATRIX    4
  49.  
  50. /* The epson specific are defined here: */
  51. #define EPSON_WIDTH        80                /* 80 char per line. */
  52. #define EPSON_PIXEL_2_CHAR    8      /* 8 pixels per char, in REG_DENSITY. */
  53.  
  54. #define EPSON_ESC        "\033"        /* Actually regular escape char. */
  55. #define EPSON_RESET        "\033@"               /* Reset the printer. */
  56. #define EPSON_VERTICAL_SPACE    "\033A\010"         /* 8/72 inch vertically. */
  57. #define EPSON_REG_DENSITY    "\033K"          /* 640 pixels per 7.5" (line). */
  58. #define EPSON_DUAL_DENSITY    "\033L"         /* 1280 pixels per 7.5" (line). */
  59.  
  60. #ifdef __MSDOS__
  61. extern unsigned int
  62.     _stklen = 16384;                 /* Increase default stack size. */
  63. #endif /* __MSDOS__ */
  64.  
  65. #ifdef SYSV
  66. static char *VersionStr =
  67.         "Gif library module,\t\tGershon Elber\n\
  68.     (C) Copyright 1989 Gershon Elber, Non commercial use only.\n";
  69. static char
  70.     *CtrlStr = "Gif2Epsn q%- d%-DitherSize!d t%-BWThreshold!d m%-Mapping!d i%- n%- p%-PrinterName!s h%- GifFile!*s";
  71. #else
  72. static char
  73.     *VersionStr =
  74.     PROGRAM_NAME
  75.     GIF_LIB_VERSION
  76.     "    Gershon Elber,    "
  77.     __DATE__ ",   " __TIME__ "\n"
  78.     "(C) Copyright 1989 Gershon Elber, Non commercial use only.\n";
  79. static char
  80.     *CtrlStr =
  81.     PROGRAM_NAME
  82.     " q%- d%-DitherSize!d t%-BWThreshold!d m%-Mapping!d i%- n%- p%-PrinterName!s h%- GifFile!*s";
  83. #endif /* SYSV */
  84.  
  85. static char
  86.     *PrinterName = NULL;
  87. /* Make some variables global, so we could access them faster: */
  88. static int
  89.     ImageNum = 0,
  90.     BackGround = 0,
  91.     DitherSize = 2, DitherFlag = FALSE,
  92.     BWThresholdFlag = FALSE, Threshold,
  93.     BWThreshold = DEFAULT_THRESHOLD,       /* Color -> BW mapping threshold. */
  94.     Mapping, MappingFlag = FALSE,
  95.     InvertFlag = FALSE,
  96.     NiceFlag = FALSE,
  97.     PrinterFlag = FALSE,
  98.     HelpFlag = FALSE,
  99.     ColorToBWMapping = C2BW_BACK_GROUND,
  100.     InterlacedOffset[] = { 0, 4, 2, 1 }, /* The way Interlaced image should  */
  101.     InterlacedJumps[] = { 8, 8, 4, 2 };    /* be read - offsets and jumps... */
  102. static GifColorType
  103.     *ColorMap;
  104.  
  105. static void EvalDitheredScanline(GifRowType *ScreenBuffer, int Row,
  106.                     int RowSize, GifRowType *DitherBuffer);
  107. static void DumpScreen2Epsn(GifRowType *ScreenBuffer,
  108.                     int ScreenWidth, int ScreenHeight);
  109. static void PutString(FILE *Prt, int DirectPrint, char *Str, int Len);
  110. static void PutString2(FILE *Prt, int DirectPrint, char *Str, int Len);
  111.  
  112. /******************************************************************************
  113. * Interpret the command line and scan the given GIF file.              *
  114. ******************************************************************************/
  115. void main(int argc, char **argv)
  116. {
  117.     int    i, j, Error, NumFiles, Size, Row, Col, Width, Height, ExtCode, Count;
  118.     GifRecordType RecordType;
  119.     GifByteType *Extension;
  120.     char **FileName = NULL;
  121.     GifRowType *ScreenBuffer;
  122.     GifFileType *GifFile;
  123.  
  124.     if ((Error = GAGetArgs(argc, argv, CtrlStr,
  125.         &GifQuitePrint, &DitherFlag, &DitherSize,
  126.         &BWThresholdFlag, &Threshold,
  127.         &MappingFlag, &Mapping,    &InvertFlag,
  128.         &NiceFlag, &PrinterFlag, &PrinterName, &HelpFlag,
  129.         &NumFiles, &FileName)) != FALSE ||
  130.         (NumFiles > 1 && !HelpFlag)) {
  131.     if (Error)
  132.         GAPrintErrMsg(Error);
  133.     else if (NumFiles > 1)
  134.         GIF_MESSAGE("Error in command line parsing - one GIF file please.");
  135.     GAPrintHowTo(CtrlStr);
  136.     exit(1);
  137.     }
  138.  
  139.     if (HelpFlag) {
  140.     fprintf(stderr, VersionStr);
  141.     GAPrintHowTo(CtrlStr);
  142.     exit(0);
  143.     }
  144.  
  145.     if (!PrinterFlag) PrinterName = "";
  146.  
  147.     if (DitherFlag) {
  148.     /* Make sure we are o.k.: */
  149.     if (DitherSize > DITHER_MAX_MATRIX) DitherSize = DITHER_MAX_MATRIX;
  150.     if (DitherSize < DITHER_MIN_MATRIX) DitherSize = DITHER_MAX_MATRIX;
  151.     }
  152.  
  153.     /* As Threshold is in [0..100] range and BWThreshold is [0..25500]: */
  154.     if (BWThresholdFlag) {
  155.     if (Threshold > 100 || Threshold < 0)
  156.         GIF_EXIT("Threshold not in 0..100 percent.");
  157.     BWThreshold = Threshold * 255;
  158.     if (BWThreshold == 0) BWThreshold = 1;   /* Overcome divide by zero! */
  159.     }
  160.  
  161.     /* No message is emitted, but mapping method is clipped to exists method.*/
  162.     if (MappingFlag) ColorToBWMapping = Mapping % C2BW_NUM_METHODS;
  163.  
  164.     if (NumFiles == 1) {
  165.     if ((GifFile = DGifOpenFileName(*FileName)) == NULL) {
  166.         PrintGifError();
  167.         exit(-1);
  168.     }
  169.     }
  170.     else {
  171.     /* Use the stdin instead: */
  172.  
  173. #ifdef __MSDOS__
  174.     setmode(0, O_BINARY);
  175. #endif /* __MSDOS__ */
  176.     if ((GifFile = DGifOpenFileHandle(0)) == NULL) {
  177.         PrintGifError();
  178.         exit(-1);
  179.     }
  180.     }
  181.  
  182.     /* Allocate the screen as vector of column of rows. We cannt allocate    */
  183.     /* the all screen at once, as this broken minded CPU can allocate up to  */
  184.     /* 64k at a time and our image can be bigger than that:             */
  185.     /* Note this screen is device independent - its the screen as defined by */
  186.     /* the GIF file parameters itself.                         */
  187.     if ((ScreenBuffer = (GifRowType *)
  188.     malloc(GifFile -> SHeight * sizeof(GifRowType *))) == NULL)
  189.         GIF_EXIT("Failed to allocate memory required, aborted.");
  190.  
  191.     Size = GifFile -> SWidth * sizeof(GifPixelType);/* Size in bytes one row.*/
  192.     if ((ScreenBuffer[0] = (GifRowType) malloc(Size)) == NULL) /* First row. */
  193.     GIF_EXIT("Failed to allocate memory required, aborted.");
  194.  
  195.     for (i = 0; i < GifFile -> SWidth; i++) /* Set its color to BackGround. */
  196.     ScreenBuffer[0][i] = GifFile -> SBackGroundColor;
  197.     for (i = 1; i < GifFile -> SHeight; i++) {
  198.     /* Allocate the other rows, andset their color to background too:   */
  199.     if ((ScreenBuffer[i] = (GifRowType) malloc(Size)) == NULL)
  200.         GIF_EXIT("Failed to allocate memory required, aborted.\n");
  201.  
  202.     memcpy(ScreenBuffer[i], ScreenBuffer[0], Size);
  203.     }
  204.  
  205.     /* Scan the content of the GIF file and load the image(s) in: */
  206.     do {
  207.     if (DGifGetRecordType(GifFile, &RecordType) == GIF_ERROR) {
  208.         PrintGifError();
  209.         exit(-1);
  210.     }
  211.     switch (RecordType) {
  212.         case IMAGE_DESC_RECORD_TYPE:
  213.         if (DGifGetImageDesc(GifFile) == GIF_ERROR) {
  214.             PrintGifError();
  215.             exit(-1);
  216.         }
  217.         Row = GifFile -> ITop; /* Image Position relative to Screen. */
  218.         Col = GifFile -> ILeft;
  219.         Width = GifFile -> IWidth;
  220.         Height = GifFile -> IHeight;
  221.         GifQprintf("\n%s: Image %d at (%d, %d) [%dx%d]:     ",
  222.             PROGRAM_NAME, ++ImageNum, Col, Row, Width, Height);
  223.         if (GifFile -> ILeft + GifFile -> IWidth > GifFile -> SWidth ||
  224.            GifFile -> ITop + GifFile -> IHeight > GifFile -> SHeight) {
  225.             fprintf(stderr, "Image %d is not confined to screen dimension, aborted.\n");
  226.             exit(-2);
  227.         }
  228.         if (GifFile -> IInterlace) {
  229.             /* Need to perform 4 passes on the images: */
  230.             for (Count = i = 0; i < 4; i++)
  231.             for (j = Row + InterlacedOffset[i]; j < Row + Height;
  232.                          j += InterlacedJumps[i]) {
  233.                 GifQprintf("\b\b\b\b%-4d", Count++);
  234.                 if (DGifGetLine(GifFile, &ScreenBuffer[j][Col],
  235.                 Width) == GIF_ERROR) {
  236.                 PrintGifError();
  237.                 exit(-1);
  238.                 }
  239.             }
  240.         }
  241.         else {
  242.             for (i = 0; i < Height; i++) {
  243.             GifQprintf("\b\b\b\b%-4d", i);
  244.             if (DGifGetLine(GifFile, &ScreenBuffer[Row++][Col],
  245.                 Width) == GIF_ERROR) {
  246.                 PrintGifError();
  247.                 exit(-1);
  248.             }
  249.             }
  250.         }
  251.         break;
  252.         case EXTENSION_RECORD_TYPE:
  253.         /* Skip any extension blocks in file: */
  254.         if (DGifGetExtension(GifFile, &ExtCode, &Extension) == GIF_ERROR) {
  255.             PrintGifError();
  256.             exit(-1);
  257.         }
  258.         while (Extension != NULL) {
  259.             if (DGifGetExtensionNext(GifFile, &Extension) == GIF_ERROR) {
  260.             PrintGifError();
  261.             exit(-1);
  262.             }
  263.         }
  264.         break;
  265.         case TERMINATE_RECORD_TYPE:
  266.         break;
  267.         default:            /* Should be traps by DGifGetRecordType. */
  268.         break;
  269.     }
  270.     }
  271.     while (RecordType != TERMINATE_RECORD_TYPE);
  272.  
  273.     /* Lets display it - set the global variables required and do it: */
  274.     BackGround = GifFile -> SBackGroundColor;
  275.     ColorMap = (GifFile -> IColorMap ? GifFile -> IColorMap :
  276.                        GifFile -> SColorMap);
  277.     DumpScreen2Epsn(ScreenBuffer, GifFile -> SWidth, GifFile -> SHeight);
  278.  
  279.     if (DGifCloseFile(GifFile) == GIF_ERROR) {
  280.     PrintGifError();
  281.     exit(-1);
  282.     }
  283. }
  284.  
  285. /*****************************************************************************
  286. * Routine to evaluate dithered scanlines out of given ones, using Size         *
  287. * dithering matrix, starting from Row. The given scanlines are NOT modified. *
  288. *****************************************************************************/
  289. static void EvalDitheredScanline(GifRowType *ScreenBuffer, int Row,
  290.                     int RowSize, GifRowType *DitherBuffer)
  291. {
  292.     static char Dither2[2][2] = {     /* See Foley & Van Dam pp. 597-601. */
  293.     { 1, 3 },
  294.     { 4, 2 }
  295.     };
  296.     static char Dither3[3][3] = {
  297.     { 7, 9, 5 },
  298.     { 2, 1, 4 },
  299.     { 6, 3, 8 }
  300.     };
  301.     static char Dither4[4][4] = {
  302.     { 1,  9,  3,  11 },
  303.     { 13, 5,  15, 7 },
  304.     { 4,  12, 2,  10 },
  305.     { 16, 8,  14, 6 }
  306.     };
  307.     int i, j, k, Level;
  308.     long Intensity;
  309.     GifColorType *ColorMapEntry;
  310.  
  311.     /* Scan the Rows (Size rows) evaluate intensity every Size pixel and use */
  312.     /* the dither matrix to set the dithered result;                 */
  313.     for (i = 0; i <= RowSize - DitherSize; i += DitherSize) {
  314.     Intensity = 0;
  315.     for (j = Row; j < Row + DitherSize; j++)
  316.         for (k = 0; k < DitherSize; k++) {
  317.         ColorMapEntry = &ColorMap[ScreenBuffer[j][i+k]];
  318.         Intensity += 30 * ((int) ColorMapEntry->Red) +
  319.                  59 * ((int) ColorMapEntry->Green) +
  320.                  11 * ((int) ColorMapEntry->Blue);
  321.         }
  322.  
  323.     /* Find the intensity level (between 0 and Size^2) of our matrix:    */
  324.     /* Expression is "Intensity * BWThreshold / (25500 * DefThresh)"     */
  325.     /* but to prevent from overflow in the long evaluation we do this:   */
  326.     Level = ((Intensity / 2550) * ((long) DEFAULT_THRESHOLD) /
  327.                         (((long) BWThreshold) * 10));
  328.     switch (DitherSize) {
  329.         case 2:     
  330.         for (j = 0; j < DitherSize; j++)
  331.             for (k = 0; k < DitherSize; k++)
  332.             DitherBuffer[j][i+k] = Dither2[j][k] <= Level;
  333.         break;
  334.         case 3:
  335.         for (j = 0; j < DitherSize; j++)
  336.             for (k = 0; k < DitherSize; k++)
  337.             DitherBuffer[j][i+k] = Dither3[j][k] <= Level;
  338.         break;
  339.         case 4:
  340.         for (j = 0; j < DitherSize; j++)
  341.             for (k = 0; k < DitherSize; k++)
  342.             DitherBuffer[j][i+k] = Dither4[j][k] <= Level;
  343.         break;
  344.     }
  345.     }
  346. }
  347.  
  348. /******************************************************************************
  349. * The real dumping routine. Few things are taken into account:              *
  350. * 1. The Nice flag. If TRUE each pixel is printed twice in double density.    *
  351. * 2. The Invert flag. If TRUE each pixel before drawn is inverted.          *
  352. * 3. The rendering mode and dither matrix flag if dithering is selected.      *
  353. *   The image is drawn from ScreenBuffer ScreenTop/Left in the bottom/right   *
  354. * directions.                                           *
  355. *   Unfortunatelly, there is a BUG in DOS that does not handle ctrl-Z          *
  356. * correctly if we open lptx: device in binary mode (should treat it as any    *
  357. * other char). Therefore I had to write to it directly using biosprint. I     *
  358. * dont like it either, and if you have better way to do it, let me know.      *
  359. ******************************************************************************/
  360. static void DumpScreen2Epsn(GifRowType *ScreenBuffer,
  361.                     int ScreenWidth, int ScreenHeight)
  362. {
  363.     int i, j, p, Size, LeftCWidth, Len, DirectPrint = 0,
  364.     DitheredLinesLeft = 0, DitheredLinesCount = 0, MapInvert[2];
  365.     char LinePrefixLen[2];                 /* Length of scan line. */
  366.     GifByteType *EpsonBuffer;
  367.     GifPixelType *Line;
  368.     GifRowType *DitherBuffer;
  369.     GifColorType *ColorMapEntry;
  370.     FILE *Prt = NULL;
  371.  
  372. #ifdef __MSDOS__
  373.     for (i = 0;     i < strlen(PrinterName); i++)
  374.     if (islower(PrinterName[i]))
  375.         PrinterName[i] = toupper(PrinterName[i]);
  376.  
  377.     if (strcmp(PrinterName, "LPT1") == 0 ||
  378.     strcmp(PrinterName, "PRN") == 0)
  379.     DirectPrint = 1;
  380.     else if (strcmp(PrinterName, "LPT2") == 0)
  381.         DirectPrint = 2;
  382.     else if (strcmp(PrinterName, "LPT3") == 0)
  383.         DirectPrint = 3;
  384. #endif /* __MSDOS__ */
  385.  
  386.     if (!DirectPrint) {
  387. #ifdef __MSDOS__
  388.     if (strlen(PrinterName) == 0) {
  389.         setmode(1, O_BINARY);      /* Make sure it is in binary mode. */
  390.         Prt = stdout;
  391.     }
  392.     else if ((Prt = fopen(PrinterName, "wb")) == NULL ||
  393.          setvbuf(Prt, NULL, _IOFBF, GIF_FILE_BUFFER_SIZE))
  394. #else
  395.     if (strlen(PrinterName) == 0)
  396.         Prt = stdout;
  397.     else if ((Prt = fopen(PrinterName, "w")) == NULL)
  398. #endif /* __MSDOS__ */
  399.         GIF_EXIT("Failed to open output (printer) file.");
  400.     }
  401.  
  402.     if ((EpsonBuffer = (GifByteType *) malloc(ScreenWidth)) == NULL)
  403.     GIF_EXIT("Failed to allocate memory required, aborted.");
  404.  
  405.     /* Allocate the buffer to save the dithered information. */
  406.     if (ColorToBWMapping == C2BW_DITHER) {
  407.     if ((DitherBuffer = (GifRowType *)
  408.         malloc(DITHER_MAX_MATRIX * sizeof(GifRowType *))) != NULL) {
  409.         Size = ScreenWidth * sizeof(GifPixelType);     /* Size of one row. */
  410.         for (i = 0; i < DITHER_MAX_MATRIX; i++) {
  411.         if ((DitherBuffer[i] = (GifRowType) malloc(Size)) == NULL) {
  412.             DitherBuffer = NULL;
  413.             break;
  414.         }
  415.         }
  416.     }
  417.     if (DitherBuffer == NULL)
  418.         GIF_EXIT("Failed to allocate memory required, aborted.");
  419.     }
  420.     else
  421.     DitherBuffer = NULL;
  422.  
  423.     /* Reset the printer, and make sure no space between adjacent lines: */
  424.     PutString(Prt, DirectPrint, EPSON_RESET, 2);
  425.     PutString(Prt, DirectPrint, EPSON_VERTICAL_SPACE, 3);
  426.  
  427.     /* Prepar left spacing to begin with, so image will be in the middle.    */
  428.     LeftCWidth = (EPSON_WIDTH - (ScreenWidth / EPSON_PIXEL_2_CHAR)) / 2;
  429.  
  430.     if (InvertFlag) {           /* Make the inversion as fast a possible. */
  431.     MapInvert[0] = 1;
  432.     MapInvert[1] = 0;
  433.     }
  434.     else {
  435.     MapInvert[0] = 0;
  436.     MapInvert[1] = 1;
  437.     }
  438.  
  439.     for (i = 0, p = 0; i < ScreenHeight; i++, p++) {
  440.     GifQprintf("\b\b\b\b%-4d", ScreenHeight - i);
  441.     Line = ScreenBuffer[i];
  442.  
  443.     /* If 8 lines were accumulated in printer buffer - dump them out.    */
  444.     if (p == 8) {
  445.         for (Len = ScreenWidth-1; Len >= 0; Len--)
  446.             if (EpsonBuffer[Len]) break;
  447.  
  448.         /* Only in case this line is not empty: */
  449.         if (Len++ >= 0) {
  450.         /* Make the left space, so image will be centered: */
  451.         for (j = 0; j < LeftCWidth; j++)
  452.             PutString(Prt, DirectPrint,  " ", 1);
  453.  
  454.         /* Full printer line is ready to be dumped - send it out: */
  455.         if (NiceFlag) {
  456.             PutString(Prt, DirectPrint, EPSON_DUAL_DENSITY, 2);
  457.             LinePrefixLen[0] = (Len * 2) % 256;
  458.             LinePrefixLen[1] = (Len * 2) / 256;
  459.             PutString(Prt, DirectPrint, LinePrefixLen, 2);
  460.             PutString2(Prt, DirectPrint, (char *) EpsonBuffer, Len);
  461.         }
  462.         else {
  463.             PutString(Prt, DirectPrint, EPSON_REG_DENSITY, 2);
  464.             LinePrefixLen[0] = Len % 256;
  465.             LinePrefixLen[1] = Len / 256;
  466.             PutString(Prt, DirectPrint, LinePrefixLen, 2);
  467.             PutString(Prt, DirectPrint, (char *) EpsonBuffer, Len);
  468.         }
  469.         }
  470.         PutString(Prt, DirectPrint, "\015\012", 2);
  471.         p = 0;
  472.     }
  473.  
  474.     /* We decide right here what method to map Colors to BW so the inner */
  475.     /* loop will be independent of it (and therefore faster):         */
  476.     switch(ColorToBWMapping) {
  477.         case C2BW_BACK_GROUND:
  478.         for (j = 0; j < ScreenWidth; j++)
  479.             EpsonBuffer[j] = (EpsonBuffer[j] << 1) +
  480.                     MapInvert[Line[j] != BackGround];
  481.         break;
  482.         case C2BW_GREY_LEVELS:
  483.         for (j = 0; j < ScreenWidth; j++) {
  484.             ColorMapEntry = &ColorMap[Line[j]];
  485.             /* For the transformation from RGB to BW, see Folley &   */
  486.             /* Van Dam pp 613: The Y channel is the BW we need:         */
  487.             /* As colors are 255 maximum, the result can be up to    */
  488.             /* 25500 which is still in range of our 16 bits integers */
  489.             EpsonBuffer[j] = (EpsonBuffer[j] << 1) +
  490.             MapInvert[(30 * (int) ColorMapEntry->Red) +
  491.                    59 * ((int) ColorMapEntry->Green) +
  492.                    11 * ((int) ColorMapEntry->Blue) >
  493.                     BWThreshold];
  494.         }
  495.         break;
  496.         case C2BW_DITHER:
  497.         if (DitheredLinesLeft-- == 0) {
  498.             EvalDitheredScanline(ScreenBuffer,
  499.             (i < ScreenHeight - DitherSize ? i :
  500.                          ScreenHeight - DitherSize),
  501.             ScreenWidth, DitherBuffer);
  502.             DitheredLinesLeft = DitherSize - 1;
  503.             DitheredLinesCount = 0;
  504.         }
  505.         Line = DitherBuffer[DitheredLinesCount++];
  506.         for (j = 0; j < ScreenWidth; j++)
  507.             EpsonBuffer[j] = (EpsonBuffer[j] << 1) +
  508.                             MapInvert[Line[j]];
  509.         break;
  510.     }
  511.     }
  512.  
  513.     /* If buffer in incomplete - complete it and dump it out: */
  514.     if (p != 0) {
  515.     for (Len = ScreenWidth - 1; Len >= 0; Len--)
  516.         if (EpsonBuffer[Len]) break;
  517.     if (Len++ >= 0) {
  518.         i = 8 - p;                     /* Amount to shift. */
  519.         for (j = 0; j < ScreenWidth; j++) EpsonBuffer[j] <<= i;
  520.  
  521.         /* Make the left space, so image will be centered: */
  522.         for (j = 0; j < LeftCWidth; j++)
  523.         PutString(Prt, DirectPrint, " ", 1);
  524.  
  525.         if (NiceFlag) {
  526.         PutString(Prt, DirectPrint, EPSON_DUAL_DENSITY, 2);
  527.         LinePrefixLen[0] = (Len * 2) % 256;
  528.         LinePrefixLen[1] = (Len * 2) / 256;
  529.         PutString(Prt, DirectPrint, LinePrefixLen, 2);
  530.         PutString2(Prt, DirectPrint, (char *) EpsonBuffer, Len);
  531.         }
  532.         else {
  533.         PutString(Prt, DirectPrint, EPSON_REG_DENSITY, 2);
  534.         LinePrefixLen[0] = Len % 256;
  535.         LinePrefixLen[1] = Len / 256;
  536.         PutString(Prt, DirectPrint, LinePrefixLen, 2);
  537.         PutString(Prt, DirectPrint, (char *) EpsonBuffer, Len);
  538.         }
  539.     }
  540.     PutString(Prt, DirectPrint, "\015\012", 2);
  541.     }
  542.  
  543.     fclose(Prt);
  544. }
  545.  
  546. /******************************************************************************
  547. * Dumps the string of given length, to Prt. No char in Str has special          *
  548. * meaning, and even zero (NULL) chars are dumped.                  *
  549. * If however DirectPrint is non zero, string is dumped to specifed lpt port.  *
  550. ******************************************************************************/
  551. static void PutString(FILE *Prt, int DirectPrint, char *Str, int Len)
  552. {
  553.     int i;
  554.  
  555.     if (DirectPrint) {
  556. #ifdef __MSDOS__
  557.     for (i = 0; i < Len; i++) biosprint(0, Str[i], DirectPrint - 1);
  558. #else
  559.     GIF_EXIT("Can not print directly to a printer if not MSDOS.");
  560. #endif /* __MSDOS__ */
  561.     }
  562.     else
  563.     for (i = 0; i < Len; i++) fputc(Str[i], Prt);
  564. }
  565.  
  566. /******************************************************************************
  567. * Dumps the string of given length, to Prt. No char in Str has special          *
  568. * meaning, and even zero (NULL) chars are dumped. Every char is dumped twice. *
  569. * If however DirectPrint is non zero, string is dumped to specifed lpt port.  *
  570. ******************************************************************************/
  571. static void PutString2(FILE *Prt, int DirectPrint, char *Str, int Len)
  572. {
  573.     int i;
  574.  
  575.     if (DirectPrint) {
  576. #ifdef __MSDOS__
  577.     for (i = 0; i < Len; i++) {
  578.         biosprint(0, Str[i], DirectPrint - 1);
  579.         biosprint(0, Str[i], DirectPrint - 1);
  580.     }
  581. #else
  582.     GIF_EXIT("Can not print directly to a printer if not MSDOS.");
  583. #endif /* __MSDOS__ */
  584.     }
  585.     else
  586.     for (i = 0; i < Len; i++) {
  587.         fputc(Str[i], Prt);
  588.         fputc(Str[i], Prt);
  589.     }
  590. }
  591.